package com.mm.util.common;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.mm.base.exception.FileException;
import com.mm.base.exception.MuMuException;
import com.mm.util.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.SparkSession;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;

import javax.net.ssl.HttpsURLConnection;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description 文件操作
 * @Date 2023/9/27 上午11:14
 * @Author yanglin
 **/
@Slf4j
public class FileUploadUtil {

    /**
     * 资源映射路径 前缀
     */
    public static final String RESOURCE_PREFIX = "/upload/";

    /**
     * 上传
     *
     * @param file
     * @param sequence
     * @return
     */
    public static String uploadFile(MultipartFile file, int sequence) {
        // 获取文件名
        String uploadFileName = file.getOriginalFilename();
        String datePath = DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMATTER);
        StringBuilder fileName = new StringBuilder();
        fileName.append(datePath).append("/").append(extractFileName(uploadFileName, sequence));
        // 文件存储路径
        StringBuilder filePath = new StringBuilder();
        String dir = getProjectPath();
        filePath.append(dir);
        File localFile = getAbsoluteFile(filePath.toString(), fileName.toString());
        try {
            FileUtil.writeBytes(file.getBytes(), localFile);
            // /20230927/20230927145332-1.jpeg
            return fileName.toString();
        } catch (Exception e) {
            FileUtil.del(localFile);
            log.error("uploadFile error: {}", e.getMessage());
            throw new FileException(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), e.getMessage());
        }
    }

    /**
     * 小说内容写入到本地
     *
     * @param title
     * @param content
     * @return
     */
    public static String writeBookFile(String title, String content) {
        String datePath = DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMATTER);
        StringBuilder fileName = new StringBuilder();
        fileName.append(datePath).append("/").append(title).append(".txt");
        // 文件存储路径
        StringBuilder filePath = new StringBuilder();
        String dir = getProjectPath();
        filePath.append(dir);
        File localFile = getAbsoluteFile(filePath.toString(), fileName.toString());
        try {
            FileUtil.writeString(content, localFile, StandardCharsets.UTF_8);
            return fileName.toString();
        } catch (Exception e) {
            FileUtil.del(localFile);
            log.error("writeBookFile error: {}", e.getMessage());
            throw new FileException(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), e.getMessage());
        }
    }

    /**
     * 下载网络文件到本地
     *
     * @param fileUrl
     * @param uploadFileName
     * @param suffix
     */
    public static String downloadNetworkFileToLocal(String fileUrl, String uploadFileName, String suffix) {
        String findFileName = "";
        String datePath = DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMATTER);
        StringBuilder fileName = new StringBuilder();
        fileName.append(datePath).append("/").append(uploadFileName + suffix);
        File localFile = getLocalFullFilePath(fileName.toString());
        try {
            log.info("downloadNetworkFileToLocal start : {}", fileName);
            URL url = new URL(fileUrl);
            URLConnection conn = url.openConnection();
            conn.setConnectTimeout(500000);
            conn.setRequestProperty(
                    "Accept",
                    "image/gif, image/jpeg, image/pjpeg, image/pjpeg, " +
                            "application/x-shockwave-flash, application/xaml+xml, " +
                            "application/vnd.ms-xpsdocument, application/x-ms-xbap, " +
                            "application/x-ms-application, application/vnd.ms-excel, " +
                            "application/vnd.ms-powerpoint, application/msword, */*");
            conn.setRequestProperty("Accept-Language", "zh-CN");
            conn.setRequestProperty("Charset", "UTF-8");
            //设置浏览器类型和版本、操作系统，使用语言等信息
            conn.setRequestProperty(
                    "User-Agent",
                    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; Trident/4.0; " +
                            ".NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; " +
                            ".NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
            //设置为长连接
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.connect();
            InputStream inputStream = null;
            HttpURLConnection httpConn = (HttpURLConnection) conn;
            int HttpResult = httpConn.getResponseCode();
            if (HttpResult != HttpURLConnection.HTTP_OK) {
                throw new MuMuException(ResultCode.SYSTEM_RESOURCE_ACCESS_ERROR);
            } else {
                inputStream = httpConn.getInputStream();
            }
            FileOutputStream fileOutputStream = new FileOutputStream(localFile);
            int byteSum = 0;
            int byteRead;
            byte[] buffer = new byte[1024];
            while ((byteRead = inputStream.read(buffer)) != -1) {
                byteSum += byteRead;
                fileOutputStream.write(buffer, 0, byteRead);
            }
            fileOutputStream.close();
            if (byteSum > 0) {
                findFileName = fileName.toString();
                log.info("downloadNetworkFileToLocal success file size : {}", byteSum);
            } else {
                FileUtil.del(localFile);
                log.error("downloadNetworkFileToLocal inputStream read fail");
            }
        } catch (Exception e) {
            FileUtil.del(localFile);
            log.error("downloadNetworkFileToLocal file : {} fail :{}", fileName.toString(), e.getMessage());
        }
        return findFileName;
    }

    /**
     * 下载网络文件到浏览器
     *
     * @param response
     * @param fileUrl
     * @param uploadFileName
     * @param suffix
     */
    public static void downloadNetworkFileToBrowser(HttpServletResponse response, String fileUrl, String uploadFileName, String suffix) {
        String fileName = uploadFileName + suffix;
        try {
            log.info("downloadNetworkFileToBrowser start : {}", fileName);
            URL url = new URL(fileUrl);
            URLConnection conn = url.openConnection();
            conn.setConnectTimeout(60000);
            conn.setRequestProperty(
                    "Accept",
                    "image/gif, image/jpeg, image/pjpeg, image/pjpeg, " +
                            "application/x-shockwave-flash, application/xaml+xml, " +
                            "application/vnd.ms-xpsdocument, application/x-ms-xbap, " +
                            "application/x-ms-application, application/vnd.ms-excel, " +
                            "application/vnd.ms-powerpoint, application/msword, */*");
            conn.setRequestProperty("Accept-Language", "zh-CN");
            conn.setRequestProperty("Charset", "UTF-8");
            //设置浏览器类型和版本、操作系统，使用语言等信息
            conn.setRequestProperty(
                    "User-Agent",
                    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.2; Trident/4.0; " +
                            ".NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; " +
                            ".NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
            //设置为长连接
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.connect();
            InputStream inputStream = null;
            HttpURLConnection httpConn = (HttpURLConnection) conn;
            int HttpResult = httpConn.getResponseCode();
            if (HttpResult != HttpURLConnection.HTTP_OK) {
                throw new MuMuException(ResultCode.SYSTEM_RESOURCE_ACCESS_ERROR);
            } else {
                inputStream = httpConn.getInputStream();
            }
            response.reset();
            response.setContentType(conn.getContentType());
            //设置文件下载方式
            response.setHeader("content-disposition","attachment;filename="+ URLEncoder.encode(fileName,"utf-8"));
            byte[] buffer = new byte[1024];
            int len;
            OutputStream outputStream = response.getOutputStream();
            while ((len = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, len);
            }
            inputStream.close();
        } catch (Exception e) {
            log.error("downloadNetworkFileToBrowser name [{}] error : {}", fileName, e.getMessage());
            throw new FileException(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), e.getMessage());
        }
    }

    /**
     * 下载本地文件到浏览器
     *
     * @param response
     * @param fileName
     * @param isLocal
     */
    public static void downloadLocalFileToBrowser(HttpServletResponse response, String fileName, boolean isLocal) {
        String filePath;
        if (isLocal) {
            filePath = getFileLocalUrl(fileName);
        } else {
            filePath = getFileNetworkUrl(fileName);
        }
        try (FileInputStream fis = new FileInputStream(filePath);
             ServletOutputStream os = response.getOutputStream()) {
            String browserFileName = fileName.substring(fileName.indexOf("/") + 1);
            //设置文件下载方式
            response.setHeader("content-disposition","attachment;filename="+ URLEncoder.encode(browserFileName,"utf-8"));
            // 设置响应的数据的格式
            // response.setContentType("image/jpeg");
            int len = 0;
            byte[] bytes = new byte[1024 * 10];
            while ((len = fis.read(bytes)) != -1) {
                os.write(bytes, 0, len);
                os.flush();
            }
        } catch (Exception e) {
            log.error("downloadLocalFileToBrowser name [{}] error : {}", fileName, e.getMessage());
            throw new FileException(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), e.getMessage());
        }
    }

    /**
     * 下载多个
     *
     * @param response
     * @param fileUrls
     * @param isLocal
     */
    public static void downloadZipFile(HttpServletResponse response, List<String> fileUrls, boolean isLocal) {
        String outputFileName = "file-" + DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_MS_FORMATTER) + ".zip";
        List<InputStream> inputStreamList = new ArrayList<>(fileUrls.size());
        InputStream inputStream;
        try (
                OutputStream outputStream = response.getOutputStream();
        ) {
            buildZipResponse(response, outputFileName);
            for (String url : fileUrls) {
                if (isLocal) {
                    inputStream = getInputStreamFromLocalUrl(url);
                } else {
                    inputStream = getInputStreamFromNetworkUrl(url);
                }
                inputStreamList.add(inputStream);
            }
            String[] paths = fileUrls.toArray(new String[fileUrls.size()]);
            InputStream[] ins = inputStreamList.toArray(new InputStream[inputStreamList.size()]);
            ZipUtil.zip(outputStream, paths, ins);
        } catch (IOException e) {
            log.error("downloadZipFile file number [{}] error : {}", fileUrls.size(), e.getMessage());
            throw new FileException(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), e.getMessage());
        }
        log.info("downloadZipFile file number [{}] success, zipFileName: {}", fileUrls.size(), outputFileName);
    }

    /**
     * 去除文件内容重复
     *
     * @param fileName
     */
    public static final void checkBookContent(String fileName){
        Map<String, String> repeatTxtMap = new ConcurrentHashMap<>();
        try {
            String filePath = getFileLocalUrl(fileName);
            // 需要处理数据的文件位置
            FileReader fileReader = new FileReader(filePath);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String readLine;
            while ((readLine = bufferedReader.readLine()) != null) {
                // 每次读取一行数据，与 map 进行比较，如果该行数据 map 中没有，就保存到 map 集合中
                String repeatValue = DigestUtil.md5Hex(readLine);
                if (repeatTxtMap.containsValue(repeatValue)) {
                    log.info("repeatTxt ： {}", readLine);
                } else {
                    repeatTxtMap.put(RandomUtil.randomNumbers(10), repeatValue);
                }
            }
        } catch (Exception e){
            log.error("checkBookContent error : {}",e.getMessage());
        }
    }

    /**
     * 下载搭配本地-获取文件全路径
     *
     * @return
     */
    public static final File getLocalFullFilePath(String fileName) {
        // 文件存储路径
        StringBuilder filePath = new StringBuilder();
        String dir = getProjectPath();
        filePath.append(dir);
        File localFile = getAbsoluteFile(filePath.toString(), fileName);
        return localFile;
    }

    /**
     * 获取返回接口文件路径
     *
     * @param fileName
     * @return
     */
    public static final String getPathFileName(String fileName) {
        return FileUploadUtil.RESOURCE_PREFIX + fileName;
    }

    /**
     * 获取文件名称
     *
     * @return
     */
    public static final String getFileName(String suffix) {
        StringBuilder fileName = new StringBuilder();
        String datePath = DateUtil.format(DateUtil.date(), DatePattern.PURE_DATE_FORMATTER);
        fileName.append(datePath).append("/").append(DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_FORMATTER)).append(suffix);
        return fileName.toString();
    }


    /**
     * 文件存储地址
     *
     * @param uploadDir
     * @param fileName
     * @return
     */
    public static final File getAbsoluteFile(String uploadDir, String fileName) {
        File desc = new File(uploadDir + File.separator + fileName);
        if (!desc.exists()) {
            if (!desc.getParentFile().exists()) {
                desc.getParentFile().mkdirs();
            }
            try {
                desc.createNewFile();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return desc;
    }

    /**
     * 编码文件名
     *
     * @param uploadFileName
     * @param sequence
     * @return
     */
    public static final String extractFileName(String uploadFileName, int sequence) {
        String suffix = StrUtil.sub(uploadFileName, uploadFileName.indexOf("."), uploadFileName.length());
        String fileName = DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_FORMATTER) + "-" + sequence + suffix;
        return fileName;
    }

    /**
     * 获取项目根路径
     *
     * @return 路径地址
     */
    public static String getProjectPath() {
        String prefix = FileUploadUtil.RESOURCE_PREFIX;
        String path = null;
        try {
            Resource resource = new ClassPathResource("");
            path = resource.getFile().getAbsolutePath();
            path = path.substring(0, path.indexOf("target") - 1);
            return path + prefix;
        } catch (Exception e) {
            log.error("getProjectPath error : {}", e.getMessage());
            path = FileUploadUtil.RESOURCE_PREFIX;
            if (IpUtil.isWindowsOS()) {
                path = "D:" + FileUploadUtil.RESOURCE_PREFIX;
            }
            return path;
        }
    }

    /**
     * 通过访问地址获取文件InputStream
     * http://192.168.200.247:19981/upload/20230927/20230927145332-1.jpeg
     *
     * @param fileName 文件访问地址
     * @return
     */
    public static InputStream getInputStreamFromNetworkUrl(String fileName) {
        URL url = null;
        InputStream is = null;
        try {
            url = new URL(getFileNetworkUrl(fileName));
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        try {
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setDoInput(true);
            conn.connect();
            is = conn.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return is;
    }

    /**
     * 通过文件地址获取文件InputStream
     *
     * @param fileName 文件路径
     * @return
     */
    public static InputStream getInputStreamFromLocalUrl(String fileName) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(getFileLocalUrl(fileName));
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
        return fis;
    }

    /**
     * 获取本地文件全路径
     *
     * @param fileName
     * @return
     */
    public static String getFileLocalUrl(String fileName) {
        return getProjectPath() + fileName;
    }

    /**
     * 获取本地文件网络访问地址
     *
     * @param fileName
     * @return
     */
    public static String getFileNetworkUrl(String fileName) {
        String ip = IpUtil.getLocalIp();
        String port = SpringContextUtil.getEnvironmentValue("server.port");
        String path = SpringContextUtil.getEnvironmentValue("server.servlet.context-path");
        StringBuilder networkUrl = new StringBuilder();
        networkUrl.append("http://").append(ip).append(":").append(port).append(path).append(FileUploadUtil.RESOURCE_PREFIX).append(fileName);
        return networkUrl.toString();
    }

    /**
     * 构建ZIP响应信息
     *
     * @param response
     * @param outputFileName
     * @throws java.io.UnsupportedEncodingException
     */
    public static void buildZipResponse(HttpServletResponse response, String outputFileName) throws UnsupportedEncodingException {
        response.reset();
        response.setHeader("Content-Type", "application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(outputFileName, "UTF-8"));
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
    }


    /**
     * MultipartFile 转 File
     *
     * @param multipartFile
     * @throws Exception
     */
    public static File multipartFileToFile(MultipartFile multipartFile) throws Exception {
        File toFile = null;
        if (Objects.nonNull(multipartFile.getInputStream())) {
            toFile = FileUtil.writeFromStream(multipartFile.getInputStream(), multipartFile.getOriginalFilename());
        }
        return toFile;
    }

    /**
     * 计算第几次分片(索引)
     *
     * @param startSize 分片开始大小
     * @return
     */
    public static int computeFragmentIndex(int startSize) {
        // 文件分片  以20MB去分片
        int shardSize = 1 * 1024 * 1024;
        // 定义分片索引
        int shardIndex = 1;
        if (startSize > 0) {
            shardIndex = startSize / shardSize;
        }
        return shardIndex;
    }

    public static void main(String[] args) throws IOException {
        //String downloadPath = "https://www.qisu12.com/api/txt_down.php?articleid=15211&amp;articlename=返虚";
        //FileUploadUtil.downloadNetworkFileToLocal(downloadPath, "返虚", ".txt");

        String fileName = "20231110/饱食终日，加点修仙.txt";
        FileUploadUtil.checkBookContent(fileName);
    }
}

